/*
 * TcpSocketViewerProtocol.cpp
 *
 *  Created on: May 25, 2011
 *      Author: madboyka
 */

#include "TcpSocketViewerProtocol.h"
#include <netdb.h>
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>

#define MAXPENDING 5    /* Max connection requests */

void TcpSocketViewerProtocol::PrintMatrixSexy(float *m)
{
	for (int i = 0; i < 4; ++i)
	{
		printf("|");
		for (int j = 0; j < 4; ++j)
		{
			printf("%2.2f ", m[i * 4 + j]);
		}
		printf("|\n");
	}
	printf("\n");
}

void TcpSocketViewerProtocol::connect(QString address)
{
	struct sockaddr_in serv_addr;
	struct hostent *server;
	socket = ::socket(AF_INET, SOCK_STREAM, 0);
	if (socket < 0)
	{
		//error handling
		throw NetworkException("Error on connect");
	}
	server = gethostbyname(address.toLocal8Bit().data());
	if (server == NULL)
	{
		//error handling
		throw NetworkException("Error on connect");
	}

	struct timeval tv; /* timeval and timeout stuff added by davekw7x */
	tv.tv_sec = 2;
	tv.tv_usec = 0;
	if (setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof(tv)))
	{
		perror("setsockopt");
	}

	bzero((char *) &serv_addr, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET;
	bcopy((char *) server->h_addr, (char *) &serv_addr.sin_addr.s_addr,
			server->h_length);

	serv_addr.sin_port = htons(PORT);
	if (::connect(socket, (struct sockaddr *) &serv_addr, sizeof(serv_addr))
			< 0)
	{
		//error handling
		throw NetworkException("Error on connect");
	}

}

float* TcpSocketViewerProtocol::getLocationData()
{
	return ldata;
}

int TcpSocketViewerProtocol::getCount()
{
	return count;
}

TcpSocketViewerProtocol::TcpSocketViewerProtocol(int clientSocket)
{
	socket = clientSocket;
}

TcpSocketViewerProtocol::TcpSocketViewerProtocol()
{
	ldata = 0;
	// TODO Auto-generated constructor stub
}

TcpSocketViewerProtocol::~TcpSocketViewerProtocol()
{
	// TODO Auto-generated constructor stub
}

int TcpSocketViewerProtocol::receive()
{
#ifdef TEMP
	float *temp = ldata;
#endif
	delete[] ldata;

	int messageType;
	if (recv(socket, &messageType, sizeof(int), 0) <= 0)
	{
		throw NetworkException("Receive error...");
	}
	//	printf("messageType: %d\n", messageType);
	if (recv(socket, &count, sizeof(int), 0) <= 0)
	{
		throw NetworkException("Error on connect");
	}

	//	printf("count: %d", count);
	switch (messageType)
	{
	case ADD_ELEMENTS:
		newElements.clear();
		for (int i = 0; i < count; ++i)
		{
			ElementData temp = readElementData();
			printf("recieve: %d\n", temp.getDataCount());
			newElements.append(temp);
			puts("Got new element data!");
		}
		break;
	case DELETE_ELEMENTS:

		break;
	case CHANGE_ELEMENTS:
		ldata = new float[count * 16];
		for (int i = 0; i < count; ++i)
		{
			int cnt = 0;
			while (recv(socket, ldata + (16 * i), sizeof(float) * 16, 0) <= 0)
			{
				cnt++;
				printf("receive: Sex van!\n");
				//				throw NetworkException("Error on connect");
				if (cnt > 10)
				{
					throw NetworkException("Failed to receive data after a few retries.");
				}
			}
		}
		break;
	}

#ifdef TEMP
	delete []temp;
#endif
	return messageType;
}

void TcpSocketViewerProtocol::bind()
{
	puts("binding address");
	struct sockaddr_in echoserver;

	/* Create the TCP socket */
	if ((socket = ::socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
	{
		puts("Failed to create socket");
		throw NetworkException("Error on bind");
	}
	int on = 1;
	setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
	/* Construct the server sockaddr_in structure */
	memset(&echoserver, 0, sizeof(echoserver)); /* Clear struct */
	echoserver.sin_family = AF_INET; /* Internet/IP */
	echoserver.sin_addr.s_addr = htonl(INADDR_ANY); /* Incoming addr */
	echoserver.sin_port = htons(PORT); /* server port */
	/* Bind the server socket */
	//		log("Going to bind socket");
	if (::bind(socket, (struct sockaddr *) &echoserver, sizeof(echoserver)) < 0)
	{
		puts("Failed to bind the server socket");
	}
	else
	{
		printf("Listening on port:%d\n", PORT);
	}
	/* Listen on the server socket */
	if (listen(socket, MAXPENDING) < 0)
	{
		puts("Failed to listen on server socket");
	}
}

ViewerProtocol *TcpSocketViewerProtocol::acceptConnection()
{
	int clientsock;
	struct sockaddr_in echoclient;

	/* Run until cancelled */
	while (1)
	{
		unsigned int clientlen = sizeof(echoclient);
		/* Wait for client connection */
		if ((clientsock = accept(socket, (struct sockaddr *) &echoclient,
				&clientlen)) < 0)
		{
			puts("Failed to accept client connection");
		}
		fprintf(stdout, "Client connected: %s\n",
				inet_ntoa(echoclient.sin_addr));
		ViewerProtocol *protocol = new TcpSocketViewerProtocol(clientsock);
		return protocol;
	}
}

void TcpSocketViewerProtocol::sendMessage(int type, int count)
{
	if (send(socket, (void*) &type, sizeof(int), 0) <= 0)
	{
		puts("Failed to send bytes to client");
	}
	if (send(socket, (void*) &count, sizeof(int), 0) <= 0)
	{
		puts("Failed to send bytes to client");
	}

}

void TcpSocketViewerProtocol::sendObjects(const void *data, int size)
{
	if (send(socket, (void*) data, size, 0) <= 0)
	{
		throw NetworkException(QString("Failed to send bytes to client"));
	}
}

void TcpSocketViewerProtocol::close()
{
	puts("Trying to close viewer!");
	if (socket > 0)
	{
		::close(socket);
		socket = 0;
	}
}

void TcpSocketViewerProtocol::waitRequest()
{
	int request;
	if (recv(socket, &request, sizeof(int), 0) <= 0)
	{
		throw NetworkException(QString("Failed to wait for request!"));
	}
}

void TcpSocketViewerProtocol::sendRequest()
{

	int request = 1;
	if (send(socket, &request, sizeof(int), 0) <= 0)
	{
		throw NetworkException("Request error");
	}
}

void TcpSocketViewerProtocol::writeElementData(const ElementData edata)
{
	ElementType etype = edata.getElementType();
	sendObjects(&etype, sizeof(ElementType));
	sendObjects(edata.getColor(), sizeof(char) * 3);
	sendObjects(edata.getTransformationMatrix(), sizeof(float) * 16);
	//	int *datacount = new int;
	//	*datacount = edata.dataCount;
	if (etype != MeshType)
	{
		int x = edata.getDataCount();
		sendObjects(&x, sizeof(int));
		sendObjects(edata.getData(), sizeof(float) * x);
	}
	else
	{
		int x = strlen(edata.getMeshType()) + 1;
		sendObjects(&x, sizeof(int));
		sendObjects(edata.getMeshType(), sizeof(int));
	}
}

ElementData TcpSocketViewerProtocol::readElementData()
{
	ElementType etype;
	if (recv(socket, &etype, sizeof(ElementType), 0) <= 0)
	{
		throw NetworkException(QString("Failed to wait for request!"));
	}
	ElementData edata(etype);
	unsigned char *colors = new unsigned char[3];
	if (recv(socket, colors, sizeof(char) * 3, 0) <= 0)
	{
		delete[] colors;
		throw NetworkException(QString("Failed to wait for request!"));
	}
	edata.setColor(colors[0], colors[1], colors[2]);
	delete[] colors;
	float* tm = new float[16];
	if (recv(socket, tm, sizeof(float) * 16, 0) <= 0)
	{
		throw NetworkException(QString("Failed to wait for request!"));
	}
	edata.setTransformationMatrix(tm);
	delete[] tm;
	int dataCount;
	if (recv(socket, &dataCount, sizeof(int), 0) <= 0)
	{
		throw NetworkException(QString("Failed to wait for request!"));
	}
	printf("tcpsocketprotocol dataCount: %d\n", dataCount);
	tm = new float[dataCount];
	for (int i = 0; i < dataCount; ++i)
	{
		if (recv(socket, tm + i, sizeof(float), 0) <= 0)
		{
			throw NetworkException(QString("Failed to wait for request!"));
		}
	}
	edata.setData(dataCount, tm);
	return edata;
}

ElementData TcpSocketViewerProtocol::getNewElement(int index)
{
	ElementData temp(newElements[index]);
	return temp;
}
